import { createClient } from '@supabase/supabase-js'
import { NextResponse } from 'next/server'
import type { FileSystemItem, CreateFileSystemItemRequest, UpdateFileSystemItemRequest } from '@/lib/types/database'
// Create server-side Supabase client with user session
function createServerSupabaseClient(request: Request) {
// Get the authorization header from the request
const authHeader = request.headers.get('authorization')
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false
},
global: {
headers: authHeader ? {
Authorization: authHeader
} : {}
}
}
)
}
// Helper function to transform flat array into hierarchical tree
function buildFileTree(items: FileSystemItem[]): (FileSystemItem & { children?: FileSystemItem[] })[] {
const itemMap = new Map<string, FileSystemItem & { children: FileSystemItem[] }>()
const roots: (FileSystemItem & { children: FileSystemItem[] })[] = []
// Create map of all items with children arrays
items.forEach(item => {
itemMap.set(item.id, { ...item, children: [] })
})
// Build tree structure
items.forEach(item => {
const itemWithChildren = itemMap.get(item.id)
if (!itemWithChildren) return
if (item.parent_id) {
const parent = itemMap.get(item.parent_id)
if (parent) {
parent.children.push(itemWithChildren)
}
} else {
roots.push(itemWithChildren)
}
})
// Sort children recursively
const sortItems = (items: (FileSystemItem & { children: FileSystemItem[] })[]) => {
items.sort((a, b) => {
// Folders first, then files
if (a.type !== b.type) {
return a.type === 'folder' ? -1 : 1
}
// Then by sort_order, then by name
if (a.sort_order !== b.sort_order) {
return (a.sort_order || 0) - (b.sort_order || 0)
}
return a.name.localeCompare(b.name)
})
// Recursively sort children
items.forEach(item => {
if (item.children.length > 0) {
sortItems(item.children as (FileSystemItem & { children: FileSystemItem[] })[])
}
})
}
sortItems(roots)
return roots
}
// GET /api/books/[id]/files - Get file tree for a book
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
try {
const supabase = createServerSupabaseClient(request)
const bookId = params.id
// Get all file system items for this book (RLS will handle access control)
const { data: items, error } = await supabase
.from('file_system_items')
.select('*')
.eq('book_id', bookId)
.order('sort_order', { ascending: true })
if (error) {
console.error('Error fetching file system items:', error)
return NextResponse.json(
{ error: 'Failed to fetch files' },
{ status: 500 }
)
}
// Build tree structure
const fileTree = buildFileTree(items || [])
return NextResponse.json({
files: fileTree,
total: items?.length || 0
})
} catch (error) {
console.error('Error in GET /api/books/[id]/files:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
// POST /api/books/[id]/files - Create a new file or folder
export async function POST(
request: Request,
{ params }: { params: { id: string } }
) {
try {
const supabase = createServerSupabaseClient(request)
const bookId = params.id
const body: CreateFileSystemItemRequest = await request.json()
// Validate required fields
if (!body.name || !body.type) {
return NextResponse.json(
{ error: 'Name and type are required' },
{ status: 400 }
)
}
// If parent_id is provided, validate it exists (RLS will handle access control)
if (body.parent_id) {
const { data: parent, error: parentError } = await supabase
.from('file_system_items')
.select('id, type')
.eq('id', body.parent_id)
.eq('book_id', bookId)
.single()
if (parentError || !parent) {
return NextResponse.json(
{ error: 'Parent folder not found' },
{ status: 400 }
)
}
if (parent.type !== 'folder') {
return NextResponse.json(
{ error: 'Parent must be a folder' },
{ status: 400 }
)
}
}
// Get next sort order
const { data: existingItems } = await supabase
.from('file_system_items')
.select('sort_order')
.eq('book_id', bookId)
.eq('parent_id', body.parent_id || null)
.order('sort_order', { ascending: false })
.limit(1)
const nextSortOrder = (existingItems?.[0]?.sort_order || 0) + 1
// Prepare item data
const itemData: any = {
book_id: bookId,
parent_id: body.parent_id || null,
name: body.name,
type: body.type,
sort_order: body.sort_order || nextSortOrder
}
// Add file-specific fields
if (body.type === 'file') {
itemData.content = body.content || ''
itemData.file_extension = body.file_extension || 'md'
itemData.mime_type = body.mime_type || 'text/markdown'
} else {
// For folders
itemData.expanded = false
}
// Create the item
const { data: newItem, error } = await supabase
.from('file_system_items')
.insert(itemData)
.select()
.single()
if (error) {
console.error('Error creating file system item:', error)
return NextResponse.json(
{ error: 'Failed to create item' },
{ status: 500 }
)
}
return NextResponse.json({
item: newItem,
message: `${body.type === 'file' ? 'File' : 'Folder'} created successfully`
})
} catch (error) {
console.error('Error in POST /api/books/[id]/files:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}